﻿using MongoDB.Driver;
using Sustainalytics.Auth.Claims;
using Sustainalytics.ClientDW.Entities;
using Sustainalytics.Entities.RiskRatings;
using Sustainalytics.RiskRatings.Service.Models;
using Sustainalytics.Utils;
using Sustainalytics.BusinessResearchProductStates.Entities.ResearchState;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Threading.Tasks;
using System.Web.Http;
using System.Web.Http.Cors;

namespace Sustainalytics.RiskRatings.Service.Controllers
{
    [EnableCors(origins: "*", headers: "*", methods: "*")]
    [RoutePrefix("api/v1/company/{companyId}")]
    public class SummaryController : ApiController
    {
        private enum ResearchAvailability {
            Available = 1,
            NotAvailable = 2,
            InProgress = 3,
            NotAccessible = 4,
            NotApplicable = 5
        };

        private readonly List<int> allowedEventCategories = new List<int> { 3, 4, 5 };
        private readonly IMongoCollection<RiskRatingMaterialEsgIssue> MEICollection;
        private readonly IMongoCollection<RiskRatingBenchmark> MEIBenchmarkCollection;
        private readonly IMongoCollection<RiskRatingResearchProfile> RiskRatingProfileCollection;
        private readonly IMongoCollection<CoreRiskRatingResearchProfile> CoreRiskRatingProfileCollection;
        private readonly IMongoCollection<RiskRatingMeiSubPeerGroupAverage> MEISubPeerGroupAverageCollection;
        private readonly IMongoCollection<CorporateData> _corporateDataCollection;
        private readonly IMongoCollection<CoreRiskRatingIndicator> _coreIndicatorCollection;
        private static readonly IndicatorCodeComparer indicatorComparer = new IndicatorCodeComparer();

        public SummaryController(IMongoCollection<RiskRatingResearchProfile> RRProfileCollection,
            IMongoCollection<CoreRiskRatingResearchProfile> CoreRRProfileCollection,
            IMongoCollection<RiskRatingMaterialEsgIssue> MEICollection,
            IMongoCollection<RiskRatingBenchmark> MEIBenchmarkCollection,
            IMongoCollection<RiskRatingMeiSubPeerGroupAverage> MEISubPeerGroupAverageCollection,
            IMongoCollection<CorporateData> corporateDataCollection,
            IMongoCollection<CoreRiskRatingIndicator> coreIndicatorCollection)
        {
            this.RiskRatingProfileCollection = RRProfileCollection;
            this.CoreRiskRatingProfileCollection = CoreRRProfileCollection;
            this.MEICollection = MEICollection;
            this.MEIBenchmarkCollection = MEIBenchmarkCollection;
            this.MEISubPeerGroupAverageCollection = MEISubPeerGroupAverageCollection;
            _corporateDataCollection = corporateDataCollection;
            _coreIndicatorCollection = coreIndicatorCollection;
        }

        [HttpGet]
        [Route("")]
        [InjectUniverse("universeIds", AccountFeature.RiskRatingsResearch)]
        public async Task<HttpResponseMessage> GetCompanyInfo(Guid companyId, [FromUri]int[] universeIds = null)
        {
            var corpData = await _corporateDataCollection
                .Find(x => x.Id == companyId)
                .Project<CorporateData>(Builders<CorporateData>.Projection
                    .Include(x => x.Id)
                    .Include(x => x.CompanyName)
                    .Include(x => x.MainExchangeTicker)
                    .Include(x => x.RiskRatingResearchState)
                    .Include(x => x.CoreRiskResearchState)
                    .Include(x => x.UniverseIds))
                .FirstOrDefaultAsync()
                .ConfigureAwait(false);

            if (corpData == null)
                return Request.CreateResponse(HttpStatusCode.NotFound);

            if (!corpData.UniverseIds.Any(x => universeIds.Contains(x)))
                return Request.CreateResponse(HttpStatusCode.OK, new
                {
                    Id = companyId,
                    corpData.CompanyName,
                    ExchangeTicker = corpData.MainExchangeTicker,
                    Availability = ResearchAvailability.NotAccessible
                });

            var companyRiskResearch = await RiskRatingProfileCollection.Find(x => x.Id == companyId).FirstOrDefaultAsync().ConfigureAwait(false);
            var companyCoreRiskResearch = await CoreRiskRatingProfileCollection.Find(x => x.Id == companyId).FirstOrDefaultAsync().ConfigureAwait(false);

            //if not found, company has no research available
            if ((companyRiskResearch == null) && (companyCoreRiskResearch == null))
            {               
                return Request.CreateResponse(HttpStatusCode.OK, new
                {
                    Id = companyId,
                    corpData.CompanyName,
                    ExchangeTicker = corpData.MainExchangeTicker,
                    Availability = ResearchAvailability.NotAvailable
                });
            }
            
            if(companyRiskResearch != null) //company has ComprehensiveRiskReasearch
            {   
                var data = await GetComprehensiveDataAsync(companyRiskResearch, companyId, corpData);
                return Request.CreateResponse(HttpStatusCode.OK, data);
            }
            else //company has CoreRiskReasearch
            {
                var data = await GetCoreDataAsync(companyCoreRiskResearch, companyId, corpData);
                return Request.CreateResponse(HttpStatusCode.OK, data);
            }
        }

        private async Task<object> GetCoreDataAsync(CoreRiskRatingResearchProfile companyCoreRiskResearch, Guid companyId, CorporateData corpData)
        {
            if (corpData.CoreRiskResearchState.ResearchState != ResearchState.ResearchAvailable)
                return new
                {
                    Id = companyId,
                    corpData.CompanyName,
                    ExchangeTicker = corpData.MainExchangeTicker,
                    Availability = _researchAvailability[corpData.CoreRiskResearchState.ResearchState]
                };

            var @indicators = await _coreIndicatorCollection.Find(e => e.CompanyId == companyId).ToListAsync().ConfigureAwait(false);
            
            return new
            {
                companyCoreRiskResearch.Id,
                companyCoreRiskResearch.CompanyName,
                companyCoreRiskResearch.Country,
                companyCoreRiskResearch.SubPeerGroupId,
                companyCoreRiskResearch.SubPeerGroup,
                companyCoreRiskResearch.PeerGroup,
                companyCoreRiskResearch.ExchangeTicker,
                Availability = ResearchAvailability.Available,
                AssessmentCode = companyCoreRiskResearch.Cluster.Code,
                Assessment = companyCoreRiskResearch.Assessment.ToString(),
                RiskRating = companyCoreRiskResearch.ManagementRisk.UnmanagedRisk.Value,
                ManagedRisk = companyCoreRiskResearch.ManagementRisk.ManagedRisk.Value,
                ManageableRisk = companyCoreRiskResearch.ManagementRisk.ManageableRisk.Value,
                companyCoreRiskResearch.ManagementRisk.Exposure,
                ExposureCluster = companyCoreRiskResearch.ExposureCluster.ToString(),
                companyCoreRiskResearch.LastUpdate,
                FinalBetaFactor = companyCoreRiskResearch.FinalBetaFactor.Value,
                ManagementRisk = companyCoreRiskResearch.ManagementRisk.Management.Value,
                ManagementCluster = companyCoreRiskResearch.ManagementCluster.ToString(),
                companyCoreRiskResearch.AnalystView,
                companyCoreRiskResearch.Sentences,
                companyCoreRiskResearch.ExposureComment,
                companyCoreRiskResearch.EsgRiskManagementComment,
                companyCoreRiskResearch.BusinessModelComment,
                subIndustryAverageExposure = 30,
                subIndustryAverageManagement = 40,
                SubIndustryExposureScore = companyCoreRiskResearch.SubIndustryExposureScore.Value,
                Indicators = @indicators?.OrderBy(x=> x.Code, indicatorComparer).Select(CreateIndicator())
            };
        }

        private async Task<object> GetComprehensiveDataAsync(RiskRatingResearchProfile companyRiskResearch, Guid companyId, CorporateData corpData)
        {
            if (corpData.RiskRatingResearchState.ResearchState != ResearchState.ResearchAvailable)
                return new
                {
                    Id = companyId,
                    corpData.CompanyName,
                    ExchangeTicker = corpData.MainExchangeTicker,
                    Availability = _researchAvailability[corpData.RiskRatingResearchState.ResearchState]
                };

            var types = new[] { MaterialIssueType.MaterialIssue, MaterialIssueType.IdiosyncraticIssue };

            //get material and idiosyncratic MEI
            var others = await MEICollection.Find(x => x.CompanyId == companyId && (types == null || types.Length == 0 || types.Contains(x.Type))).SortBy(x => x.Type).ToListAsync().ConfigureAwait(false);
            var meiAverage = await MEISubPeerGroupAverageCollection.Find(x => x.SubPeerGroupId == companyRiskResearch.SubPeerGroupId).ToListAsync().ConfigureAwait(false);
            var baselineMei = companyRiskResearch.MaterialEsgIssues?.FirstOrDefault(x => x.Type == MaterialIssueType.BaselineIssue && x.Indicators != null);
            var companyBenchmark= await MEIBenchmarkCollection.Find(y =>y.Id==companyId).FirstOrDefaultAsync().ConfigureAwait(false);
            return new
            {
                companyRiskResearch.Id,
                companyRiskResearch.CompanyName,
                companyRiskResearch.Country,
                companyRiskResearch.SubPeerGroupId,
                companyRiskResearch.SubPeerGroup,
                companyRiskResearch.PeerGroup,
                companyRiskResearch.ExchangeTicker,
                Assessment = companyRiskResearch.Assessment.ToString(),
                AssessmentCode = companyRiskResearch.Cluster.Code,
                Availability = ResearchAvailability.Available,
                RiskRating = companyRiskResearch.ManagementRisk.UnmanagedRisk.Value,
                ManageableRisk = companyRiskResearch.ManagementRisk.ManageableRisk.Value,
                ManagedRisk = companyRiskResearch.ManagementRisk.ManagedRisk.Value,
                Exposure = companyRiskResearch.ManagementRisk.Exposure.Value,
                OverallBetaFactor = companyRiskResearch.OverallBetaFactor?.Value,
                OverallExcessExposure = companyRiskResearch.OverallExcessExposure?.Value,
                Management = companyRiskResearch.ManagementRisk.Management.Value,
                companyRiskResearch.LastUpdate,
                ExposureCluster = companyRiskResearch.ExposureCluster?.ToString(),
                ManagementCluster = companyRiskResearch.ManagementCluster?.ToString(),
                SubIndustryAverageExposure = companyBenchmark.CompanyBenchmark.SubIndustryPerformanceExposure.Average,
                SubIndustryAverageManagement = companyBenchmark.CompanyBenchmark.SubIndustryPerformanceManagement.Average,
                SubIndustryExposureScore = companyRiskResearch.SubIndustryExposureScore.Value,
                //Analyst view comments
                Comments = new
                {
                    BusinessModel = companyRiskResearch.BusinessModelComment?.Value,
                    Exposure = companyRiskResearch.ExposureComment?.Value,
                    OverallExposureAssessment = companyRiskResearch.OverallExposureAssessment?.Value,
                    ESGRiskManagement = companyRiskResearch.EsgRiskManagementComment?.Value,
                    AnalystViewOverallAssessment = companyRiskResearch.AnalystViewOverallAssessment?.Value,
                    OverallManagementAssessment = companyRiskResearch.OverallManagementAssessment?.Value
                },
                //MEI list
                Issues = new
                {
                    //Baseline MEI
                    Baseline = baselineMei != null ? new MeiDto(baselineMei, meiAverage) : null,
                    Others = others?.OrderBy(x => x.SortKey.Value).Select(x => new MeiDto(x, meiAverage, true, allowedEventCategories))
                }
            };
        }

        private readonly Dictionary<ResearchState, ResearchAvailability> _researchAvailability = new Dictionary<ResearchState, ResearchAvailability>()
        {
            { ResearchState.FrameworkNotApplicable, ResearchAvailability.NotApplicable },
            { ResearchState.ResearchAvailable, ResearchAvailability.Available          },
            { ResearchState.ResearchInProgress, ResearchAvailability.InProgress        },
            { ResearchState.ResearchNotAvailable, ResearchAvailability.NotAvailable    },
        };

        private static Func<CoreRiskRatingIndicator, object> CreateIndicator()
        {
            return indicator => new
            {
                indicator.Name,
                indicator.IndicatorType,
                Code = indicator.Code.Replace('.', '_'),
                RawScore = indicator.RawScore.Value,
                FinalWeight = indicator.FinalWeight.Value,
                WeightedScore = indicator.WeightedScore.Value,
                Category = indicator.AnswerCategory.Value
            };
        }
    }
}